package org.rcgwt.client.util;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.JavaScriptObject;

/**
 * <p>
 * Implementation of the {@link java.util.regex.Pattern} class with a wrapper
 * aroung the Javascript <a href="http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Regular_Expressions"
 * >RegExp</a> object. As most of the methods delegate to the JavaScript RegExp
 * object, certain differences in the declaration and behaviour of regular
 * expressions must be expected.
 * </p>
 * <p>
 * Please note that neither the {@link java.util.regex.Pattern#compile(String)}
 * method nor {@link Matcher} instances are supported. For the later, consider
 * using {@link Pattern#match(String)}.
 * </p>
 * 
 * @author George Georgovassilis
 * 
 */
public class Pattern {

	/**
	 * Declares that regular expressions should be matched across line borders.
	 */
	public final static int MULTILINE = 1;

	/**
	 * Declares that characters are matched reglardless of case.
	 */
	public final static int CASE_INSENSITIVE = 2;

	private final JavaScriptObject regExp;

	private static JavaScriptObject createExpression(String pattern, int flags) {
		String sFlags = "";
		if ((flags & MULTILINE) != 0)
			sFlags += "m";
		if ((flags & CASE_INSENSITIVE) != 0)
			sFlags += "i";
		return _createExpression(pattern, sFlags);
	}

	private static native JavaScriptObject _createExpression(String pattern,
			String flags) /*-{
		return new RegExp(pattern, flags);
	}-*/;

	private native void _match(String text, List<String> matches) /*-{
		var regExp = this.@org.rcgwt.client.util.Pattern::regExp;
		var result = text.match(regExp);
		if (result == null) return;
		for (var i=0;i<result.length;i++)
		matches.@java.util.ArrayList::add(Ljava/lang/Object;)(result[i]);
	}-*/;

	/**
	 * Determines wether the specified regular expression is validated by the
	 * provided input.
	 * 
	 * @param regex
	 *            Regular expression
	 * @param input
	 *            String to validate
	 * @return <code>true</code> if matched.
	 */
	public static boolean matches(String regex, String input) {
		return new Pattern(regex).matches(input);
	}

	/**
	 * Escape a provided string so that it will be interpreted as a literal in
	 * regular expressions. The current implementation does escape each
	 * character even if not neccessary, generating verbose literals.
	 */
	public static String quote(String input) {
		String output = "";
		for (int i = 0; i < input.length(); i++) {
			output += "\\" + input.charAt(i);
		}
		return output;
	}

	/**
	 * Class constructor
	 * 
	 * @param pattern
	 *            Regular expression
	 */
	public Pattern(String pattern) {
		this(pattern, 0);
	}

	/**
	 * Class constructor
	 * 
	 * @param pattern
	 *            Regular expression
	 * @param flags
	 */
	public Pattern(String pattern, int flags) {
		regExp = createExpression(pattern, flags);
	}

	public static Pattern compile(String pattern) {
		return new Pattern(pattern);
	}

	/**
	 * This method is borrowed from the JavaScript RegExp object. It parses a
	 * string and returns as an array any assignments to parenthesis groups in
	 * the pattern's regular expression
	 * 
	 * @param text
	 * @return Array of strings following java's Pattern convention for groups:
	 *         Group 0 is the entire input string and the remaining groups are
	 *         the matched parenthesis. In case nothing was matched a NULL is
	 *         returned.
	 */
	public String[] match(String text) {
		List<String> matches = new ArrayList<String>();
		_match(text, matches);

		if (matches.isEmpty()) {
			return null;
		}

		return matches.toArray(new String[matches.size()]);
	}

	/**
	 * Determines wether a provided text matches the regular expression
	 */
	public native boolean matches(String text) /*-{
		var regExp = this.@org.rcgwt.client.util.Pattern::regExp;
		return regExp.test(text);
	}-*/;

	/**
	 * Returns the regular expression for this pattern
	 * 
	 * 
	 */
	public native String pattern() /*-{
		var regExp = this.@org.rcgwt.client.util.Pattern::regExp;
		return regExp.source;
	}-*/;

	private native void _split(String input, List<String> results) /*-{
		var regExp = this.@org.rcgwt.client.util.Pattern::regExp;
		var parts = input.split(regExp);
		for (var i=0;i<parts.length;i++)
		results.@java.util.ArrayList::add(Ljava/lang/Object;)(parts[i]  );
	}-*/;

	/**
	 * Split an input string by the pattern's regular expression
	 * 
	 * @param input
	 * @return Array of strings
	 */
	public String[] split(String input) {
		List<String> results = new ArrayList<String>();
		_split(input, results);

		return results.toArray(new String[results.size()]);
	}

}